package com.orion.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.orion.domain.Result;
import com.orion.enumeration.Oauth2ExceptionCodeEnum;
import com.orion.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;


/**
 * 检验/oauth2/authorize中的scope、clientId，response_type,用户名密码自然有basicAuthenticationFilter验证
 * @author Administrator
 */
@Component
@Slf4j
public class CustomBasicAuthenticationFilter extends OncePerRequestFilter {

    private static final String OAUTH2_AUTHORIZATION_URL = "/oauth/authorize";

    @Resource
    private ClientDetailsService clientDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        if (!request.getRequestURI().equals(OAUTH2_AUTHORIZATION_URL)) {
            filterChain.doFilter(request, response);
            return;
        }

        String scope = request.getParameter(OAuth2Utils.SCOPE);
        String clientId = request.getParameter(OAuth2Utils.CLIENT_ID);
        String responseType = request.getParameter(OAuth2Utils.RESPONSE_TYPE);


        if (StrUtil.isBlank(scope) || StrUtil.isBlank(clientId) || StrUtil.isBlank(responseType)) {
            log.info("missing authorize oauth2 param");
            Result r = Oauth2ExceptionCodeEnum.INVALID_REQUEST.toResult();
            ResponseUtil.jsonResp(response).getWriter().write(JSONUtil.toJsonStr(r));
            return;
        }

        if (!"token".equals(responseType) && !request.getMethod().equals(HttpMethod.POST.name())) {
            log.info("authorize request only post");
            Result r = Oauth2ExceptionCodeEnum.AUTHORIZE_UNSUPPORTED_REQUEST_METHOD.toResult();
            ResponseUtil.jsonResp(response).getWriter().write(JSONUtil.toJsonStr(r));
            return;
        }

        Set<String> responseTypes = OAuth2Utils.parseParameterList(responseType);


        ClientDetails clientDetails;
        try {
            clientDetails = this.clientDetailsService.loadClientByClientId(clientId);
        } catch (ClientRegistrationException e) {
            log.info("clientId [{}] not found ",clientId);
            Result r = Oauth2ExceptionCodeEnum.INVALID_CLIENT.toResult();
            ResponseUtil.jsonResp(response).getWriter().write(JSONUtil.toJsonStr(r));
            return;
        }

        Set<String> scopes = clientDetails.getScope();
        if (!scopes.contains(scope)){
            log.info("invalid scope [{}]",scope);
            Result r = Oauth2ExceptionCodeEnum.INVALID_SCOPE.toResult();
            ResponseUtil.jsonResp(response).getWriter().write(JSONUtil.toJsonStr(r));
            return;
        }

        if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            log.info("Unsupported response types: {}" , responseTypes);
            Result r = Oauth2ExceptionCodeEnum.INVALID_GRANT.toResult();
            ResponseUtil.jsonResp(response).getWriter().write(JSONUtil.toJsonStr(r));
            return;
        }

        filterChain.doFilter(request,response);
    }
}

